package com.io.netty.simple;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/*通道（channel）中装的是数据，是用来传输数据的，更多的是为了进行数据的读写
管道（pipeline）中装的是处理器，处理器是用来处理业务逻辑的
管道和通道是可以进行相互转换的
* */
public class NettyServer {
    public static void main(String[] args) throws Exception {


        //创建BossGroup 和 WorkerGroup
        //说明
        //1. 创建两个线程组 bossGroup 和 workerGroup，注意：两个对象类型是一样的
        //2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理，会交给 workerGroup完成
        //3. 两个都是无限循环
        //4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数
        //   默认实际 cpu核数 * 2
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(); //8



        try {
            //创建服务器端的启动对象，配置参数
            ServerBootstrap bootstrap = new ServerBootstrap();

            //使用链式编程来进行设置
            bootstrap.group(bossGroup, workerGroup) //设置两个线程组
                    .channel(NioServerSocketChannel.class) //使用NioSocketChannel 作为服务器的通道实现
                    .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列和客户端的连接个数
                    .childOption(ChannelOption.SO_KEEPALIVE, true) //设置保持活动连接状态的客户端
//                    .handler(null) // 该 handler对应 bossGroup , childHandler 对应 workerGroup
                    .childHandler(new ChannelInitializer<SocketChannel>() {//创建一个通道初始化对象(匿名对象)

                        //客户端传过来的数据会通过这个初始化的通道传到服务端，服务端会调用initChannel方法
                        //来利用SocketChannel的对象ch来处理从客户端传过来的数据
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //可以使用一个集合管理 SocketChannel， 再推送消息时，可以将业务
                            // 加入到各个channel 对应的 NIOEventLoop 的 taskQueue或者 scheduleTaskQueue
                            System.out.println("客户socketchannel hashcode=" + ch.hashCode());

                            //给pipeline 设置处理器，处理业务逻辑使用的是pipeline中的处理器
                            //处理器是自己定义的，或者是原生的（好像是有原生的）
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    }); // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器

            System.out.println(".....服务器 is ready...");

            //当前线程并不会阻塞在这，这个是由new NioEventLoopGroup(1)来维护的，那个线程就负责监听客户端的连接
            //cf.addListener来调用监听器
            //监听器会调用ChannelFutureListener对象的operationComplete方法来进行监听
            //注意：这个是监听成功之后就会返回ChannelFuture对象
            //并不是连接客户端成功之后才会返回，之前一直理解错了
            ChannelFuture cf = bootstrap.bind(6668).sync();

            //给cf 注册监听器，监控我们关心的事件
            cf.addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (cf.isSuccess()) {
                        System.out.println("监听端口 6668 成功");
                    } else {
                        System.out.println("监听端口 6668 失败");
                    }
                }
            });

            //监听通道，当关闭通道的命令发出后，该段代码会执行，并将通道关闭
            cf.channel().closeFuture().sync();
        }finally {//断开连接并关闭两个线程组
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

}
